package org.moelab.trafficlight.incoming.socks5;

import org.moelab.trafficlight.incoming.socks5.authenticator.Socks5PasswordAuthenticator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.channel.*;
import io.netty.handler.codec.socksx.SocksVersion;
import io.netty.handler.codec.socksx.v5.*;

/**
 * @author xp
 */
@ChannelHandler.Sharable
public class Socks5InitialRequestHandler extends SimpleChannelInboundHandler<DefaultSocks5InitialRequest> {

    private static final Logger LOG = LoggerFactory.getLogger(Socks5InitialRequestHandler.class);

    private final Socks5IncomingConfig config;

    public Socks5InitialRequestHandler(Socks5IncomingConfig config) {
        this.config = config;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DefaultSocks5InitialRequest msg) throws Exception {
        ChannelPipeline pipeline = ctx.pipeline();
        pipeline.remove(Socks5InitialRequestDecoder.class.getName());
        pipeline.remove(this);

        if (msg.decoderResult().isFailure() || msg.version() == null) {
            if (LOG.isInfoEnabled()) {
                LOG.info("message decode failed");
            }
            // transfer to next handler
            ctx.fireChannelRead(msg);
        } else {
            SocksVersion version = msg.version();
            boolean success = false;
            if (SocksVersion.SOCKS5.equals(version)) {
                success = handleSocks5(ctx, msg);
            } else if (SocksVersion.SOCKS4a.equals(version)) {
                success = handleSocks4(ctx, msg);
            }
            if (!success) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("unsupported version");
                }
                ctx.close();
            }
        }
    }

    private boolean handleSocks5(ChannelHandlerContext ctx, DefaultSocks5InitialRequest msg) {
        Socks5PasswordAuthenticator authenticator = config.getAuthenticator();
        Socks5AuthMethod authMethod = authenticator.authMethod();
        if (msg.authMethods().contains(authMethod)) {
            authenticator.inject(ctx);
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("no acceptable auth method: " + msg.authMethods());
            }
            authMethod = Socks5AuthMethod.UNACCEPTED;
        }
        Socks5InitialResponse response = new DefaultSocks5InitialResponse(authMethod);
        ctx.writeAndFlush(response);
        return true;
    }

    private boolean handleSocks4(ChannelHandlerContext ctx, DefaultSocks5InitialRequest msg) {
        return false;
    }
}
